package org.yunai.swjg.server.module.item;

import org.slf4j.Logger;
import org.yunai.swjg.server.core.constants.SysMessageConstants;
import org.yunai.swjg.server.entity.ItemEntity;
import org.yunai.swjg.server.module.item.container.Bag;
import org.yunai.swjg.server.module.item.container.CommonBag;
import org.yunai.swjg.server.module.item.container.EquipmentBag;
import org.yunai.swjg.server.module.item.container.ShoulderBag;
import org.yunai.swjg.server.module.item.template.ItemTemplate;
import org.yunai.swjg.server.module.item.vo.Item;
import org.yunai.swjg.server.module.partner.vo.Partner;
import org.yunai.swjg.server.module.player.vo.Player;
import org.yunai.swjg.server.rpc.message.S_C.S_C_BagItemDelResp;
import org.yunai.swjg.server.rpc.message.S_C.S_C_BagItemUpdateReq;
import org.yunai.swjg.server.rpc.message.S_C.S_C_BagUpdateReq;
import org.yunai.swjg.server.rpc.message.S_C.S_C_SysMessageReq;
import org.yunai.swjg.server.rpc.struct.StBagItemDel;
import org.yunai.swjg.server.rpc.struct.StBagItemUpdate;
import org.yunai.yfserver.common.LoggerFactory;
import org.yunai.yfserver.spring.BeanManager;

import java.util.*;

/**
 * 背包清单
 * User: yunai
 * Date: 13-4-6
 * Time: 下午3:04
 */
public class Inventory {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoggerFactory.Logger.item, Inventory.class);

    private static ItemMapper itemMapper;

    static {
        itemMapper = BeanManager.getBean(ItemMapper.class);
    }

    /**
     * 默认背包大小
     * TODO 需要改呀改下
     */
    public static final int DEFAULT_CAPACITY = 6;

    /**
     * 玩家
     */
    private Player player;

    /**
     * 玩家/伙伴背包集合
     */
    private final Map<Integer, Map<Bag.BagType, CommonBag>> roleBags;

    public Inventory(Player player) {
        this.player = player;
        this.roleBags = new HashMap<>();
    }

    public void putItem(Item item) {
        CommonBag bag = item.getWearId() == Item.OWNER_ID_NONE ? getBag(player.getId(), item.getBagType()) :
                getBag(item.getWearId(), item.getBagType());
        if (bag != null) {
            // TODO check道具
            bag.putItem(item);
        } else {
            // TODO log
        }
    }

//    public EquipmentBag getPlayerEquipBag() {
//        return playerEquipBag;
//    }

    /**
     * @return 玩家主背包
     */
    public ShoulderBag getPrimBag() {
        return (ShoulderBag) roleBags.get(player.getId()).get(Bag.BagType.PRIM);
    }

    /**
     * @return 玩家仓库背包
     */
    public ShoulderBag getDeportBag() {
        return (ShoulderBag) roleBags.get(player.getId()).get(Bag.BagType.DEPOT);
    }

    @Deprecated
    public CommonBag getBag(Bag.BagType bagType) {
        throw new UnsupportedOperationException("该方法被移除，请使用{@link getBag(int, Bag.BagType)");
//        return bags.get(bagType); // TODO 需要改
    }

    /**
     * 获得角色的背包<br />
     * 约定：当roleId为0时，就认为该值为{@link #player}的编号，即，当前玩家的编号
     *
     * @param roleId  角色编号
     * @param bagType 背包类型
     * @return 背包
     */
    public CommonBag getBag(int roleId, Bag.BagType bagType) {
        if (roleId == Item.OWNER_ID_NONE) {
            roleId = player.getId();
        }
        return roleBags.get(roleId).get(bagType);
    }

    public Item getItem(int roleId, Bag.BagType bagType, int bagIndex) {
        return getBag(roleId, bagType).getByIndex(bagIndex);
    }

    // =================== [业务方法] ===================

    /**
     * 初始化并数据加载<br />
     * 该方法需要在
     * <pre>
     *     {@link org.yunai.swjg.server.module.player.vo.Player#load()}
     *     {@link org.yunai.swjg.server.module.partner.PartnerDiary#load()}
     * </pre>
     * 后调用，因为该方法会初始化玩家和伙伴背包
     */
    public void load() {
        init();
        // 加载数据
        List<ItemEntity> itemEntities = itemMapper.selectList(player.getId());
        for (ItemEntity entity : itemEntities) {
            Item item = Item.build(entity, player);
            putItem(item);
        }
    }

    private void init() {
        // 玩家背包
        addPlayerBag();
        // 伙伴背包
        for (Partner partner : player.getPartnerDiary().getAll()) {
            addPartnerBag(partner);
        }
    }

    private void addPlayerBag() {
        Map<Bag.BagType, CommonBag> eachRoleBags = new HashMap<>();
        roleBags.put(player.getId(), eachRoleBags);
        eachRoleBags.put(Bag.BagType.DEPOT, new ShoulderBag(player, Bag.BagType.DEPOT, DEFAULT_CAPACITY)); // TODO 背包容量未实现
        eachRoleBags.put(Bag.BagType.PRIM, new ShoulderBag(player, Bag.BagType.PRIM, DEFAULT_CAPACITY));
        eachRoleBags.put(Bag.BagType.EQUIPMENT, new EquipmentBag(player, player));
    }

    public void addPartnerBag(Partner partner) {
        Map<Bag.BagType, CommonBag> eachRoleBags = new HashMap<>();
        roleBags.put(partner.getId(), eachRoleBags);
        eachRoleBags.put(Bag.BagType.EQUIPMENT, new EquipmentBag(player, partner));
    }

    /**
     * 交换两个背包的位置
     *
     * @param itemA 道具A
     * @param itemB 道具B
     */
    public void swap(Item itemA, Item itemB) {
        // 验证
        if (itemA == itemB) {
            return;
        }
        // 交换
        int indexA = itemA.getIndex();
        if (itemA.getBagType() != itemB.getBagType()) {
            Bag.BagType bagTypeA = itemA.getBagType();
            itemA.changeBagType(itemB.getBagType(), itemB.getIndex());
            itemB.changeBagType(bagTypeA, indexA);
            putItem(itemA);
            putItem(itemB);
        } else {
            itemA.changeIndex(itemB.getIndex());
            itemB.changeIndex(indexA);
        }
    }


    public List<Item> addItem(Bag.BagType bagType, int templateId, int count) {
        return addItems(bagType, Arrays.asList(new ItemParam(templateId, count)));
    }

    public List<Item> addItems(Bag.BagType bagType, Collection<ItemParam> itemParams) {
        // 去除Collection里道具数量为不合法的
        Iterator<ItemParam> iterator = itemParams.iterator();
        while (iterator.hasNext()) {
            if (iterator.next().getCount() <= 0) {
                iterator.remove();
            }
        }
        if (itemParams.isEmpty()) {
            return new ArrayList<>(0);
        }
        // 检查背包是否足够
        if (checkBagSpaceEnough(bagType, itemParams, false)) {
            return new ArrayList<>(0);
        }
        // 道具添加
        List<Item> changedItems = new ArrayList<>(itemParams.size());
        ShoulderBag bag = (ShoulderBag) getBag(player.getId(), bagType);
        for (ItemParam itemParam : itemParams) {
            ItemTemplate template = ItemTemplate.getItemTemplate(itemParam.getTemplateId());
            if (template == null) {
                LOGGER.error("[addItems] [player({}) add item({})  * num({}) not found]", player.getId(), itemParam.getTemplateId(), itemParam.getCount());
                continue;
            }
            List<Item> subChangedItems = bag.addItem(template, itemParam.getCount());
            if (subChangedItems.isEmpty()) { // 该处若添加道具没产生道具变化，则认为是错误的
                LOGGER.error("[addItems] [player({}) add item({}) * num({}) error.]", player.getId(), itemParam.getTemplateId(), itemParam.getCount());
                continue;
            }
            changedItems.addAll(subChangedItems);
        }
        return changedItems;
    }

    public boolean checkBagSpaceEnough(Bag.BagType bagType, Collection<ItemParam> itemParams, boolean showError) {
        ShoulderBag bag = (ShoulderBag) getBag(player.getId(), bagType);
        // 计算需要空格
        int needSlot = 0;
        for (ItemParam itemParam : itemParams) {
            ItemTemplate template = ItemTemplate.getItemTemplate(itemParam.getTemplateId());
            if (template == null) {
                LOGGER.error("[checkBagSpaceEnough] [player({}) template({}) not found]", player.getId(), itemParam.getTemplateId());
                continue;
            }
            needSlot += bag.getNeedSlot(template, itemParam.getCount());
        }
        // 判断
        int slot = bag.getEmptySlotCount();
        if (needSlot > slot) {
            if (showError) {
                player.message(new S_C_SysMessageReq(SysMessageConstants.ITEM_BAG_FULL));
            }
            return false;
        }
        return true;
    }

    public List<Item> removeItem(Bag.BagType bagType, int templateId, int count) {
        return removeItems(bagType, Arrays.asList(new ItemParam(templateId, count)));
    }

    public List<Item> removeItems(Bag.BagType bagType, Collection<ItemParam> itemParams) {
        // 去除Collection里道具数量为不合法的
        Iterator<ItemParam> iterator = itemParams.iterator();
        while (iterator.hasNext()) {
            if (iterator.next().getCount() <= 0) {
                iterator.remove();
            }
        }
        if (itemParams.isEmpty()) {
            return new ArrayList<>(0);
        }
        // 道具是否足够
        if (checkBagItemEnough(bagType, itemParams, false)) {
            return new ArrayList<>(0);
        }
        // 道具移除
        List<Item> changedItems = new ArrayList<>(itemParams.size());
        ShoulderBag primBag = getPrimBag();
        for (ItemParam itemParam : itemParams) {
            ItemTemplate template = ItemTemplate.getItemTemplate(itemParam.getTemplateId());
            if (template == null) {
                LOGGER.error("[removeItems] [player({}) remove item({})  * num({}) not found]", player.getId(), itemParam.getTemplateId(), itemParam.getCount());
                continue;
            }
            List<Item> subChangedItems = primBag.removeItem(template, itemParam.getCount());
            if (subChangedItems.isEmpty()) { // 该处若添加道具没产生道具变化，则认为是错误的
                LOGGER.error("[removeItems] [player({}) removeItem item({}) * num({}) error.]", player.getId(), itemParam.getTemplateId(), itemParam.getCount());
                continue;
            }
            changedItems.addAll(subChangedItems);
        }
        return changedItems;
    }

    public boolean checkBagItemEnough(Bag.BagType bagType, Collection<ItemParam> itemParams, boolean showError) {
        ShoulderBag bag = (ShoulderBag) getBag(player.getId(), bagType);
        for (ItemParam itemParam : itemParams) {
            if (bag.getCountByTemplateId(itemParam.getTemplateId()) < itemParam.getCount()) {
                if (showError) {
                    player.message(new S_C_SysMessageReq(SysMessageConstants.ITEM_NOT_ENOUGH));
                }
                return false;
            }
        }
        return true;
    }

    /**
     * 输出所有背包消息
     */
    public void noticeInfo() {
        for (Map.Entry<Integer, Map<Bag.BagType, CommonBag>> eachRoleBags : roleBags.entrySet()) {
            int ownerId = eachRoleBags.getKey();
            for (CommonBag bag : eachRoleBags.getValue().values()) {
                sendBagMessage(bag);
            }
        }
    }

    /**
     * 通过道具编号查找一个道具。
     * 该方法会遍历所有背包，所以慎用。
     *
     * @param id 道具编号
     * @return 道具
     */
    public Item getItemById(int id) {
        Item item;
        for (Map<Bag.BagType, CommonBag> eachRoleBags : roleBags.values()) {
            for (CommonBag bag : eachRoleBags.values()) {
                item = bag.getById(id);
                if (item != null) {
                    return item;
                }
            }
        }
        return null;
    }

    public void sendModifyMessage(Item item) {
        sendModifyMessage(Arrays.asList(item));
    }

    public void sendModifyMessage(List<Item> items) {
        List<StBagItemUpdate> updates = new ArrayList<>(items.size());
        List<StBagItemDel> deletes = new ArrayList<>(items.size());
        for (Item item : items) {
            if (Item.isEmpty(item)) {
                deletes.add(item.genStBagItemDel());
            } else {
                updates.add(item.genStBagItemUpdate());
            }
        }
        if (!updates.isEmpty()) {
            player.message(new S_C_BagItemUpdateReq(updates));
        }
        if (!deletes.isEmpty()) {
            player.message(new S_C_BagItemDelResp(deletes));
        }
    }

    public void sendBagMessage(CommonBag bag) {
        List<StBagItemUpdate> items = new ArrayList<>(bag.getCapacity());
        for (Item item : bag.getAll()) {
            items.add(item.genStBagItemUpdate());
        }
        player.message(new S_C_BagUpdateReq(bag.getBagType().get(), (byte) bag.getCapacity(),
                items, bag.getOwner().getId()));
    }
}